/*
Continuous Testing Toolkit
Copyright (C) 2009 Paul R. Holser, Jr.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package org.infinitest.toolkit;

import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

/**
 * Support structure for testing a class's compliance with the {@link Object#equals(Object) equals} and
 * {@link Object#hashCode() hashCode} contract more strictly -- use this for classes that should compare
 * equal only to objects of the same runtime class.
 *
 * {@link #equalButDifferentClass()} should return objects of a subclass of the classes of {@link #equal()} and
 * {@link #notEqual()}, that has the same values as the instances returned from {@link #equal()}.  If the
 * class under test is {@code final}, use {@link EqualsHashCodeTestSupport} instead.
 *
 * @author <a href="http://www.pholser.com">Paul Holser</a>
 */
public abstract class StrictEqualsHashCodeTestSupport extends EqualsHashCodeTestSupport {
    private Object equalButDifferentClass;

    /**
     * @return a new object of a subclass of the classes of {@link #equal()} and {@link #notEqual()}, that has the
     * same values as the instances returned from {@link #equal()}.  The cheap way to get this is to return a new
     * instance of an empty anonymous subclass of the class of {@link #equal()}.
     */
    protected abstract Object equalButDifferentClass() throws Exception;

    @Before
    public void initializeStrictHarness() throws Exception {
        equalButDifferentClass = equalButDifferentClass();

        assertNotNull("equalButDifferentClass() returned null", equalButDifferentClass);

        assertNotEquals("1st equal and equal-but-different-class instances of same classes", firstEqual.getClass(),
            equalButDifferentClass.getClass());
        assertNotEquals("2nd equal and equal-but-different-class instances of same classes",
            secondEqual.getClass(), equalButDifferentClass.getClass());
        assertNotEquals("3rd equal and equal-but-different-class instances of same classes", thirdEqual.getClass(),
            equalButDifferentClass.getClass());
        assertNotEquals("not-equal and equal-but-different-class instances of same classes", notEqual.getClass(),
            equalButDifferentClass.getClass());

        assertThat("equal-but-different-class not subclass of 1st equal class",
            equalButDifferentClass, instanceOf(firstEqual.getClass()));
        assertThat("equal-but-different-class not subclass of 2nd equal class",
            equalButDifferentClass, instanceOf(secondEqual.getClass()));
        assertThat("equal-but-different-class not subclass of 3rd equal class",
            equalButDifferentClass, instanceOf(thirdEqual.getClass()));
        assertThat("equal-but-different-class not subclass of not-equal class",
            equalButDifferentClass, instanceOf(notEqual.getClass()));
    }

    @Test
    public void subclassInstanceWithSameValuesShouldNotCompareEqualToSuperclassInstance() {
        assertNotEquals("1st vs. equal-but-different-class", firstEqual, equalButDifferentClass);
        assertNotEquals("2nd vs. equal-but-different-class", secondEqual, equalButDifferentClass);
        assertNotEquals("3rd vs. equal-but-different-class", thirdEqual, equalButDifferentClass);
        assertNotEquals("equal-but-different-class vs. 1st", equalButDifferentClass, firstEqual);
        assertNotEquals("equal-but-different-class vs. 2nd", equalButDifferentClass, secondEqual);
        assertNotEquals("equal-but-different-class vs. 3rd", equalButDifferentClass, thirdEqual);
    }
}
